using B.S.Medical.Api.Read.Application.Command.DoctorSchedule;
using B.S.Medical.Api.Read.DTOS;
using B.S.Medical.Domain.Department;
using B.S.Medical.Domain.Doctor;
using B.S.Medical.Domain.Medical;
using B.S.Medical.ErrorCode;
using B.S.Medical.Infrastructure.Interface;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace B.S.Medical.Api.Read.Application.Handler.DoctorSchedule
{
    /// <summary>
    /// 根据科室ID查询有排班医生的处理器
    /// </summary>
    public class GetDoctorsByDepartmentHandler : IRequestHandler<GetDoctorsByDepartmentCommand, ApiResult<List<DoctorScheduleInfoDTO>>>
    {
        private readonly IBaseRepository<DoctorScheduleModel> scheduleRepo;
        private readonly IBaseRepository<DoctorModel> doctorRepo;
        private readonly IBaseRepository<JobTitleModel> jobTitleRepo;
        private readonly IBaseRepository<RegisterModel> registerRepo;
        private readonly IBaseRepository<CampusModel> campusRepo;
        private readonly IBaseRepository<ManageModel> departmentRepo;
        private readonly ILogger<GetDoctorsByDepartmentHandler> logger;

        /// <summary>
        /// 构造函数
        /// </summary>
        public GetDoctorsByDepartmentHandler(
            IBaseRepository<DoctorScheduleModel> scheduleRepo,
            IBaseRepository<DoctorModel> doctorRepo,
            IBaseRepository<JobTitleModel> jobTitleRepo,
            IBaseRepository<RegisterModel> registerRepo,
            IBaseRepository<CampusModel> campusRepo,
            IBaseRepository<ManageModel> departmentRepo,
            ILogger<GetDoctorsByDepartmentHandler> logger)
        {
            this.scheduleRepo = scheduleRepo;
            this.doctorRepo = doctorRepo;
            this.jobTitleRepo = jobTitleRepo;
            this.registerRepo = registerRepo;
            this.campusRepo = campusRepo;
            this.departmentRepo = departmentRepo;
            this.logger = logger;
        }

        /// <summary>
        /// 处理根据科室ID查询有排班医生的请求
        /// </summary>
        public async Task<ApiResult<List<DoctorScheduleInfoDTO>>> Handle(GetDoctorsByDepartmentCommand request, CancellationToken cancellationToken)
        {
            try
            {
                // 获取科室信息
                var department = await departmentRepo.GetValue(request.DepartmentId);
                if (department == null || department.IsDeleted)
                {
                    return new ApiResult<List<DoctorScheduleInfoDTO>>
                    {
                        Code = ApiEnum.Fail,
                        Msg = "未找到指定科室信息",
                        Data = new List<DoctorScheduleInfoDTO>()
                    };
                }

                // 获取指定科室和日期的排班信息
                var schedules = await scheduleRepo.GetAll()
                    .Where(s => !s.IsDeleted && 
                           s.DepartmentId == request.DepartmentId && 
                           s.ScheduleDate.Date == request.ScheduleDate.Date)
                    .ToListAsync(cancellationToken);

                // 记录日志以便调试
                logger.LogInformation($"找到 {schedules.Count} 条排班记录，科室ID: {request.DepartmentId}, 日期: {request.ScheduleDate.Date}");

                if (schedules.Count == 0)
                {
                    return new ApiResult<List<DoctorScheduleInfoDTO>>
                    {
                        Code = ApiEnum.Success,
                        Msg = "该科室当天没有医生排班",
                        Data = new List<DoctorScheduleInfoDTO>()
                    };
                }

                // 获取排班医生的ID列表
                var doctorIds = schedules.Select(s => s.DoctorId).Distinct().ToList();
                
                // 记录日志以便调试
                logger.LogInformation($"找到 {doctorIds.Count} 个不同的医生ID");

                // 获取医生信息
                var doctors = await doctorRepo.GetAll()
                    .Where(d => !d.IsDeleted && doctorIds.Contains(d.Id))
                    .ToListAsync(cancellationToken);
                
                // 记录日志以便调试
                logger.LogInformation($"找到 {doctors.Count} 条医生记录");
                
                // 如果没有找到医生，直接返回空列表
                if (doctors.Count == 0)
                {
                    return new ApiResult<List<DoctorScheduleInfoDTO>>
                    {
                        Code = ApiEnum.Success,
                        Msg = "未找到相关医生信息",
                        Data = new List<DoctorScheduleInfoDTO>()
                    };
                }

                // 获取职称信息
                var jobTitles = await jobTitleRepo.GetAll()
                    .Where(j => !j.IsDeleted)
                    .ToListAsync(cancellationToken);
                
                // 获取院区信息
                var campuses = await campusRepo.GetAll()
                    .Where(c => !c.IsDeleted)
                    .ToListAsync(cancellationToken);

                // 获取挂号信息（用于计算剩余号源）
                var registers = await registerRepo.GetAll()
                    .Where(r => !r.IsDeleted && 
                           r.VisitDate.Date == request.ScheduleDate.Date && 
                           doctorIds.Contains(r.DoctorId))
                    .ToListAsync(cancellationToken);

                // 构建返回结果
                var result = new List<DoctorScheduleInfoDTO>();

                foreach (var doctor in doctors)
                {
                    // 获取医生当天的排班
                    var doctorSchedules = schedules.Where(s => s.DoctorId == doctor.Id).ToList();
                    
                    // 获取医生职称
                    var jobTitle = jobTitles.FirstOrDefault(j => j.Id == doctor.JobTitleId)?.JobTitle ?? string.Empty;
                    
                    // 获取医生所在院区信息
                    var campus = campuses.FirstOrDefault(c => c.Id == doctor.CampusId);
                    
                    // 计算总号源数
                    int totalSlots = doctorSchedules.Sum(s => s.SlotCount);
                    
                    // 计算已预约数量
                    int appointedCount = registers.Count(r => r.DoctorId == doctor.Id);
                    
                    // 计算剩余号源数
                    int remainingSlots = Math.Max(0, totalSlots - appointedCount);

                    // 创建医生排班信息DTO
                    var doctorScheduleInfo = new DoctorScheduleInfoDTO
                    {
                        DoctorId = doctor.Id,
                        DoctorName = doctor.DoctorName,
                        JobTitle = jobTitle,
                        Briefly = doctor.Briefly,
                        Photo = doctor.Photo,
                        Copayfee = doctor.Copayfee ?? 0,
                        CampusId = doctor.CampusId,
                        CampusName = campus?.CampusName ?? string.Empty,
                        CampusAddr = campus?.CampusAddr ?? string.Empty,
                        ScheduleDate = request.ScheduleDate,
                        TotalSlotCount = totalSlots,
                        RemainingSlotCount = remainingSlots,
                        // 添加科室信息
                        DepartmentId = department.Id,
                        DepartmentName = department.DepartmentName,
                        DepartmentAddr = department.DepartmentAddr
                    };

                    // 添加排班ID到列表
                    doctorScheduleInfo.ScheduleIds.AddRange(doctorSchedules.Select(s => s.Id));

                    // 获取当前时间，用于判断排班是否已过期
                    var currentTime = DateTime.Now;
                    bool isToday = request.ScheduleDate.Date == currentTime.Date;

                    // 处理上午排班信息
                    var morningSchedule = doctorSchedules.FirstOrDefault(s => s.Period == 1);
                    if (morningSchedule != null && morningSchedule.StartTime.HasValue && morningSchedule.EndTime.HasValue)
                    {
                        // 计算上午已预约数量
                        int morningAppointed = registers.Count(r => r.DoctorId == doctor.Id && 
                                                                   r.Schedule_id == morningSchedule.Id);
                        
                        // 计算上午剩余号源
                        int morningRemaining = Math.Max(0, morningSchedule.SlotCount - morningAppointed);
                        
                        // 如果是当天，且当前时间已经超过了上午排班的结束时间，则标记为无号源
                        bool isPastMorningSchedule = isToday && 
                            currentTime > request.ScheduleDate.Date.Add(morningSchedule.EndTime.Value.TimeOfDay);
                        
                        doctorScheduleInfo.MorningSchedule = new SchedulePeriodDTO
                        {
                            ScheduleId = morningSchedule.Id,
                            StartTime = morningSchedule.StartTime.Value.ToString("HH:mm"),
                            EndTime = morningSchedule.EndTime.Value.ToString("HH:mm"),
                            TotalSlots = morningSchedule.SlotCount,
                            RemainingSlots = isPastMorningSchedule ? 0 : morningRemaining,
                            HasSchedule = true,
                            IsExpired = isPastMorningSchedule
                        };
                    }
                    else
                    {
                        doctorScheduleInfo.MorningSchedule = new SchedulePeriodDTO
                        {
                            HasSchedule = false
                        };
                    }

                    // 处理下午排班信息
                    var afternoonSchedule = doctorSchedules.FirstOrDefault(s => s.Period == 2);
                    if (afternoonSchedule != null && afternoonSchedule.StartTime.HasValue && afternoonSchedule.EndTime.HasValue)
                    {
                        // 计算下午已预约数量
                        int afternoonAppointed = registers.Count(r => r.DoctorId == doctor.Id && 
                                                                     r.Schedule_id == afternoonSchedule.Id);
                        
                        // 计算下午剩余号源
                        int afternoonRemaining = Math.Max(0, afternoonSchedule.SlotCount - afternoonAppointed);
                        
                        // 如果是当天，且当前时间已经超过了下午排班的结束时间，则标记为无号源
                        bool isPastAfternoonSchedule = isToday && 
                            currentTime > request.ScheduleDate.Date.Add(afternoonSchedule.EndTime.Value.TimeOfDay);
                        
                        doctorScheduleInfo.AfternoonSchedule = new SchedulePeriodDTO
                        {
                            ScheduleId = afternoonSchedule.Id,
                            StartTime = afternoonSchedule.StartTime.Value.ToString("HH:mm"),
                            EndTime = afternoonSchedule.EndTime.Value.ToString("HH:mm"),
                            TotalSlots = afternoonSchedule.SlotCount,
                            RemainingSlots = isPastAfternoonSchedule ? 0 : afternoonRemaining,
                            HasSchedule = true,
                            IsExpired = isPastAfternoonSchedule
                        };
                    }
                    else
                    {
                        doctorScheduleInfo.AfternoonSchedule = new SchedulePeriodDTO
                        {
                            HasSchedule = false
                        };
                    }

                    // 添加到结果列表
                    result.Add(doctorScheduleInfo);
                    
                    // 记录日志
                    logger.LogInformation($"医生 {doctor.DoctorName} (ID: {doctor.Id}) 的号源情况: " +
                                         $"总号源: {totalSlots}, 已预约: {appointedCount}, 剩余: {remainingSlots}");
                }

                return new ApiResult<List<DoctorScheduleInfoDTO>>
                {
                    Code = ApiEnum.Success,
                    Msg = "获取科室医生排班信息成功",
                    Data = result
                };
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "获取科室医生排班信息时发生错误");
                return new ApiResult<List<DoctorScheduleInfoDTO>>
                {
                    Code = ApiEnum.Error,
                    Msg = "获取科室医生排班信息失败：" + ex.Message,
                    Data = new List<DoctorScheduleInfoDTO>()
                };
            }
        }
    }
} 